package com.changge.common.core.utils;

import com.alibaba.fastjson.JSON;
import jakarta.annotation.PostConstruct;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.lionsoul.ip2region.xdb.Searcher;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Map;

import static com.changge.common.core.constant.IpConst.*;


/**
 * Ip地址工具类
 *
 * @author zhangrongkang
 * @since 2023/3/15
 */
@Slf4j
@Component
public class IpUtil {

    /**
     * IP内存查询对象
     */
    private static Searcher searcher;

    /**
     * 项目启动时将Ip离线地址库加载到内存中
     */
    @PostConstruct
    private static void initIpResource() {
        try {
            InputStream inputStream = new ClassPathResource("ip2region/ip2region.xdb").getInputStream();
            byte[] dbBinStr = FileCopyUtils.copyToByteArray(inputStream);
            // 创建一个完全基于内存的查询对象
            searcher = Searcher.newWithBuffer(dbBinStr);
            // 输出加载成功日志
            log.info("create ip content cached searcher success");
        } catch (Exception e) {
            log.error("failed to create ip content cached searcher: %s", e);
        }
    }

    /**
     * 获取客户端IP地址
     *
     * @param request HttpServletRequest对象
     * @return IP地址
     */
    public static String getIpAddress(HttpServletRequest request) {
        String ipAddress = request.getHeader("x-forwarded-for");
        if (!StringUtils.hasLength(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if (!StringUtils.hasLength(ipAddress) || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if (!StringUtils.hasLength(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("HTTP_CLIENT_IP");
        }
        if (!StringUtils.hasLength(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        // 判断IP获取结果，转为合法的IP
        if (!StringUtils.hasLength(ipAddress) || LOOP_BACK_HOST.equals(ipAddress)) {
            ipAddress = LOCAL_HOST;
        }
        return ipAddress;
    }

    /**
     * 获取IP地址所属国家
     *
     * @param ipAddress IP地址
     * @return IP地址所属国家
     */
    public static String getIpCountry(String ipAddress) {
        String ipRegion = getIpRegion(ipAddress);
        // 返回对应IP属地国家
        return ipRegion.substring(0, ipRegion.indexOf("|"));
    }

    /**
     * 解析ip地址
     * <p>国内只返回 省 + 市，国外返回 国 + 具体地址</p>
     *
     * @param ipAddress ip地址
     * @return 解析后的ip地址
     */
    public static String getIpLocation(String ipAddress) {
        // 内网登录情况
        if (ipAddress.equals(LOCAL_HOST)) {
            return LOCAL_NAME;
        }
        // 获取IP区域
        String ipRegion = getIpRegion(ipAddress);
        // 定义返回结果
        StringBuilder result = new StringBuilder();
        // 拆分对应
        String[] ipSplit = ipRegion.split("\\|");
        // 判断是否为国内
        if (ipSplit[0].equals(CHINA_LOCATION)) {
            // 拼接IP地址信息
            result.append(ipSplit[2]).append(ipSplit[3]);
            return result.toString();
        }
        // 拼接国外地址，先判断是否能够将内容全部解析出来
        result.append(ipSplit[0]);
        if (ipSplit[2].equals(ZERO)) {
            return result.toString();
        }
        result.append(ipSplit[2]);
        if (ipSplit[3].equals(ZERO)) {
            return result.toString();
        }
        return result.append(ipSplit[3]).toString();
    }

    /**
     * 获取IP所属区域
     *
     * @param ipAddress IP地址
     * @return IP所属区域
     */
    private static String getIpRegion(String ipAddress) {
        // 定义IP属地返回对象
        String region = null;
        try {
            // 查询内存获取IP区域
            region = searcher.search(ipAddress);
        } catch (Exception e) {
            log.error("failed to search IpLocation: {}", ipAddress);
        }
        // 返回查询内容
        return region;
    }

    /**
     * 解析ip地址
     *
     * @param ipAddress ip地址
     * @return 解析后的ip地址
     */
    private static String getIpByNetwork(String ipAddress) {
        if (LOCAL_HOST.equals(ipAddress) || LOOP_BACK_HOST.equals(ipAddress)) {
            return LOCAL_NAME;
        }
        try {
            URL url = new URL("http://opendata.baidu.com/api.php?query=" + ipAddress + "&co=&resource_id=6006&oe=utf8");
            BufferedReader reader = new BufferedReader(new InputStreamReader(url.openConnection().getInputStream(), "utf-8"));
            String line = null;
            StringBuilder result = new StringBuilder();
            while ((line = reader.readLine()) != null) {
                result.append(line);
            }
            reader.close();
            Map map = JSON.parseObject(result.toString(), Map.class);
            List<Map<String, String>> data = (List) map.get("data");
            String location = data.get(0).get("location");
            return location.contains(" ") ? location.substring(0, location.indexOf(" ")) : location;
        } catch (Exception e) {
            return "";
        }
    }

    /**
     * 获取主机名
     *
     * @return 本地主机名
     */
    public static String getHostName() {
        try {
            // 获取本机名称
            return InetAddress.getLocalHost().getHostName();
        } catch (UnknownHostException e) {
            return UNKNOWN_HOST_NAME;
        }
    }

    public static void main(String[] args) {
        initIpResource();

        String ip = "1.32.232.0";
        String chinaIp = "127.0.0.1";
        long start = System.currentTimeMillis();
        System.out.println(getIpLocation(chinaIp));
        long cost = System.currentTimeMillis() - start;
        System.out.println(cost);
    }
}